import cv2
import numpy as np
import threading
import time
import logging
import os
from datetime import datetime
from .database import FaceDatabase
from .utils import get_faces_dir, generate_timestamp, DEFAULT_CONFIG

class SimpleRecognizer:
    """简单的人脸识别器，使用直方图比较方法"""
    
    def __init__(self):
        self.face_data = []
        self.face_labels = []
        self.label_names = {}
    
    def train(self, faces, labels):
        """训练识别器"""
        self.face_data = faces
        self.face_labels = labels
    
    def predict(self, face_img):
        """预测人脸身份"""
        if len(self.face_data) == 0 or len(self.face_labels) == 0:
            raise ValueError("识别器未经训练")
        
        # 将图像大小标准化
        face_img = cv2.resize(face_img, (100, 100))
        
        # 计算多种特征以提高准确性
        face_hist = cv2.calcHist([face_img], [0], None, [64], [0, 256])
        cv2.normalize(face_hist, face_hist, 0, 1, cv2.NORM_MINMAX)
        
        # 找到最匹配的人脸
        min_dist = float('inf')
        best_label = -1
        
        for i, face in enumerate(self.face_data):
            # 标准化尺寸
            face = cv2.resize(face, (100, 100))
            
            # 计算直方图
            hist = cv2.calcHist([face], [0], None, [64], [0, 256])
            cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
            
            # 计算直方图相似度 (相关性越高越好)
            # 使用相关性方法，值越大越相似
            corr = cv2.compareHist(face_hist, hist, cv2.HISTCMP_CORREL)
            dist = 1.0 - corr  # 转换为距离度量，越小越相似
            
            if dist < min_dist:
                min_dist = dist
                best_label = int(self.face_labels[i])  # 确保标签是整数
        
        # 转换距离为置信度得分 (0-100)
        # 相关性距离范围是0-1，转换为百分比
        confidence_percent = (1.0 - min_dist) * 100
        
        return best_label, 100 - confidence_percent  # 返回兼容的格式：标签和距离(越小越好)

class FaceRecognition:
    def __init__(self, config=None):
        """初始化人脸识别模块"""
        # 加载配置
        self.config = DEFAULT_CONFIG.copy()
        if config:
            self.config.update(config)
            
        # 初始化人脸检测器
        cascade_path = cv2.data.haarcascades + self.config['face_cascade_file']
        self.face_cascade = cv2.CascadeClassifier(cascade_path)
        if self.face_cascade.empty():
            raise ValueError("无法加载人脸检测器，请确保OpenCV正确安装")
            
        # 初始化数据库
        self.database = FaceDatabase()
        
        # 初始化识别器
        self.recognizer = SimpleRecognizer()
        self.train_recognizer()
        
        logging.info("人脸识别模块初始化完成")
    
    def train_recognizer(self):
        """训练人脸识别器"""
        face_data = []
        face_labels = []
        label_names = {}
        
        # 加载所有人脸数据
        for idx, person in enumerate(self.database.get_all_persons()):
            person_id = idx + 1  # 从1开始的ID
            label_names[person_id] = person['name']
            
            for face_path in person['face_images']:
                if os.path.exists(face_path):
                    try:
                        face_img = cv2.imread(face_path, cv2.IMREAD_GRAYSCALE)
                        if face_img is not None:
                            face_data.append(face_img)
                            face_labels.append(person_id)
                    except Exception as e:
                        logging.error(f"加载人脸图像失败 {face_path}: {e}")
        
        # 如果有足够的数据，训练识别器
        if len(face_data) > 0:
            self.recognizer.train(face_data, np.array(face_labels))
            self.recognizer.label_names = label_names
            self.label_names = label_names
            logging.info(f"识别器训练完成，使用了 {len(face_data)} 张人脸图像")
        else:
            logging.warning("没有可用的人脸数据进行训练")
    
    def add_person(self, name):
        """添加新人脸到数据库"""
        # 打开摄像头
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            return False, "无法打开摄像头"
        
        face_count = 0
        max_faces = self.config['max_faces_per_person']
        face_images = []
        
        try:
            print(f"正在为 {name} 采集人脸图像，请面对摄像头...")
            print(f"需要采集 {max_faces} 张图像，已采集: 0/{max_faces}")
            
            while face_count < max_faces:
                ret, frame = cap.read()
                if not ret:
                    break
                    
                # 显示当前帧
                cv2.imshow('Face Capture', frame)
                
                # 转换为灰度图
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                
                # 检测人脸
                faces = self.face_cascade.detectMultiScale(
                    gray,
                    scaleFactor=self.config['scale_factor'],
                    minNeighbors=self.config['min_neighbors'],
                    minSize=self.config['min_face_size']
                )
                
                # 如果检测到人脸，保存图像
                if len(faces) == 1:  # 只处理单个人脸
                    (x, y, w, h) = faces[0]
                    
                    # 绘制人脸框
                    cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                    cv2.imshow('Face Capture', frame)
                    
                    # 等待指定时间，避免连续采集相似图像
                    if cv2.waitKey(int(self.config['face_capture_interval'] * 1000)) & 0xFF == ord('q'):
                        break
                    
                    # 提取人脸区域
                    face_img = gray[y:y+h, x:x+w]
                    
                    # 保存人脸图像
                    timestamp = generate_timestamp()
                    face_filename = f"{name}_{timestamp}_{face_count}.jpg"
                    face_path = os.path.join(get_faces_dir(), face_filename)
                    
                    cv2.imwrite(face_path, face_img)
                    face_images.append(face_path)
                    
                    face_count += 1
                    print(f"需要采集 {max_faces} 张图像，已采集: {face_count}/{max_faces}")
                
                # 按q键退出
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
                
        finally:
            # 释放资源
            cap.release()
            cv2.destroyAllWindows()
        
        # 如果采集到足够的人脸，添加到数据库
        if face_count > 0:
            success, message = self.database.add_person(name, face_images)
            if success:
                self.train_recognizer()  # 重新训练识别器
            return success, message
        else:
            return False, "未能采集到人脸图像"
    
    def recognize_face(self, frame):
        """识别单帧图像中的人脸"""
        if not hasattr(self, 'label_names') or not self.label_names:
            return None
            
        # 转换为灰度图
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # 检测人脸
        faces = self.face_cascade.detectMultiScale(
            gray,
            scaleFactor=self.config['scale_factor'],
            minNeighbors=self.config['min_neighbors'],
            minSize=self.config['min_face_size']
        )
        
        if len(faces) == 0:
            return None
        
        # 对每个检测到的人脸进行识别
        max_confidence = 0
        recognized_person = None
        
        for (x, y, w, h) in faces:
            face_img = gray[y:y+h, x:x+w]
            
            try:
                # 进行人脸识别
                label, confidence = self.recognizer.predict(face_img)
                
                # OpenCV的置信度是差异值，越小越好，转换为百分比
                confidence_percent = 100 - min(100, confidence)
                
                if confidence_percent > self.config['recognition_threshold'] and confidence_percent > max_confidence:
                    max_confidence = confidence_percent
                    recognized_person = {
                        'person_name': self.label_names[label],
                        'confidence': confidence_percent,
                        'position': (x, y, w, h)
                    }
            except Exception as e:
                logging.error(f"人脸识别错误: {e}")
                continue
        
        return recognized_person
    
    def recognize_with_timeout(self, timeout=3, show_preview=False):
        """在指定时间内尝试识别人脸"""
        result = None
        stop_recognition = threading.Event()
        
        def recognition_thread():
            nonlocal result
            cap = cv2.VideoCapture(0)
            if not cap.isOpened():
                logging.error("无法打开摄像头")
                return
            
            try:
                while not stop_recognition.is_set():
                    ret, frame = cap.read()
                    if not ret:
                        break
                    
                    # 显示预览（可选）
                    if show_preview:
                        try:
                            cv2.imshow('Face Recognition', frame)
                        except Exception as e:
                            logging.warning(f"无法显示预览窗口: {e}")
                    
                    # 尝试识别人脸
                    recognized = self.recognize_face(frame)
                    if recognized:
                        result = recognized
                        break
                    
                    # 检查是否按下q键
                    if show_preview and cv2.waitKey(1) & 0xFF == ord('q'):
                        break
                        
            finally:
                cap.release()
                if show_preview:
                    try:
                        cv2.destroyAllWindows()
                    except:
                        pass
        
        # 启动识别线程
        thread = threading.Thread(target=recognition_thread)
        thread.start()
        
        # 等待指定时间
        thread.join(timeout)
        
        # 停止识别
        stop_recognition.set()
        if thread.is_alive():
            thread.join(1)  # 额外等待1秒确保线程结束
        
        return result 